#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
编码器标签页模块
实现文件选择、二维码生成和播放功能
"""

import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import threading
import os
import time
from PIL import Image, ImageTk

from qr_encoder import QREncoder


class SenderTab:
    def __init__(self, parent):
        self.parent = parent
        self.encoder = QREncoder()
        self.logger = None
        self.progress_var = None
        self.progress_label = None
        self.qr_files = []
        self.playing = False
        self.play_thread = None
        self.create_widgets()

    def create_widgets(self):
        """创建编码器界面"""
        self.frame = ttk.Frame(self.parent)
        self.frame.pack(fill='both', expand=True)
        
        # 文件选择区域
        file_frame = ttk.LabelFrame(self.frame, text="文件选择", padding="8")
        file_frame.pack(fill='x', padx=8, pady=5)
        
        ttk.Label(file_frame, text="选择文件:").grid(row=0, column=0, sticky='w', padx=5, pady=5)
        
        self.file_path_var = tk.StringVar()
        self.file_entry = ttk.Entry(file_frame, textvariable=self.file_path_var)
        self.file_entry.grid(row=0, column=1, sticky='ew', padx=5, pady=5)
        
        ttk.Button(file_frame, text="浏览", command=self.browse_file).grid(row=0, column=2, padx=5, pady=5)
        # 配置列权重使文本框自适应宽度
        file_frame.columnconfigure(1, weight=1)
        
        # 二维码信息区域
        info_frame = ttk.LabelFrame(self.frame, text="二维码信息", padding="8")
        info_frame.pack(fill='x', padx=8, pady=5)
        
        self.qr_count_label = ttk.Label(info_frame, text="预计需要二维码数量: 0")
        self.qr_count_label.pack(fill='x', padx=5, pady=5)
        
        # 操作按钮区域
        button_frame = ttk.Frame(self.frame)
        button_frame.pack(fill='x', padx=8, pady=5)
        
        self.generate_btn = ttk.Button(button_frame, text="生成二维码", command=self.generate_qr)
        self.generate_btn.pack(side=tk.LEFT, padx=5)
        
        self.play_btn = ttk.Button(button_frame, text="播放二维码", command=self.play_qr, state=tk.DISABLED)
        self.play_btn.pack(side=tk.LEFT, padx=5)
        
        # 文件路径改变时更新二维码数量
        self.file_path_var.trace('w', self.update_qr_count)
        
        # 检查现有二维码文件
        self._check_existing_qr_files()
        
    def set_logger(self, logger):
        """设置日志器"""
        self.logger = logger
        
    def set_progress(self, progress_var, progress_label):
        """设置进度条"""
        self.progress_var = progress_var
        self.progress_label = progress_label
        
    def browse_file(self):
        """浏览选择文件"""
        filename = filedialog.askopenfilename(
            title="选择文件",
            filetypes=[
                ("所有文件", "*.*"),
                ("文本文件", "*.txt"),
                ("图片文件", "*.jpg;*.png;*.gif"),
                ("文档文件", "*.doc;*.docx;*.pdf")
            ]
        )
        if filename:
            self.file_path_var.set(filename)
            if self.logger:
                    self.logger.info(f"已选择文件: {filename}")
            
    def update_qr_count(self, *args):
        """更新二维码数量估计"""
        file_path = self.file_path_var.get()
        if file_path and os.path.exists(file_path):
            try:
                file_size = os.path.getsize(file_path)
                filename = os.path.basename(file_path)
                
                # 预估压缩效果
                estimated_compressed_size = self._estimate_compressed_size(file_path, file_size, filename)
                count = self.encoder.estimate_qr_count(file_size, filename, estimated_compressed_size)
                
                # 计算每个二维码的容量
                filename_bytes = filename.encode('utf-8')
                filename_length = len(filename_bytes)
                header_size = 1 + filename_length + 2 + 2  # 文件名长度1字节 + 文件名 + 序号2字节 + 总数2字节
                payload_size = self.encoder.max_data_per_qr - header_size
                
                # 格式化文件大小显示
                if file_size < 1024:
                    size_str = f"{file_size}字节"
                elif file_size < 1024 * 1024:
                    size_str = f"{file_size/1024:.1f}KB"
                else:
                    size_str = f"{file_size/(1024*1024):.1f}MB"
                
                # 显示压缩信息
                compression_info = ""
                if estimated_compressed_size < file_size:
                    compression_ratio = (1 - estimated_compressed_size / file_size) * 100
                    compression_info = f" (压缩后约{estimated_compressed_size}字节，压缩率{compression_ratio:.1f}%)"
                
                # 检查是否超出编码能力（限制为65535个二维码）
                max_qr_limit = 65535
                if count > max_qr_limit:
                    self.qr_count_label.config(
                        text=f"文件大小: {size_str}{compression_info}, 每个二维码容量: {payload_size}字节, 需要二维码个数: {count} (超出编码能力上限{max_qr_limit}个)",
                        foreground="red"
                    )
                    self.generate_btn.config(state=tk.DISABLED)
                else:
                    self.qr_count_label.config(
                        text=f"文件大小: {size_str}{compression_info}, 每个二维码容量: {payload_size}字节, 预计需要二维码个数: {count}",
                        foreground="black"
                    )
                    self.generate_btn.config(state=tk.NORMAL)
                    
            except Exception as e:
                self.qr_count_label.config(text="无法估计二维码数量", foreground="red")
                self.generate_btn.config(state=tk.DISABLED)
                if self.logger:
                    self.logger.error(f"估计二维码数量失败: {str(e)}")
        else:
            self.qr_count_label.config(text="预计需要二维码数量: 0", foreground="black")
            self.generate_btn.config(state=tk.NORMAL)
            
    def generate_qr(self):
        """生成二维码"""
        file_path = self.file_path_var.get()
        if not file_path or not os.path.exists(file_path):
            messagebox.showerror("错误", "请先选择有效的文件")
            return
            
        # 获取文件信息
        try:
            file_size = os.path.getsize(file_path)
            filename = os.path.basename(file_path)
            
            # 预估压缩效果
            estimated_compressed_size = self._estimate_compressed_size(file_path, file_size, filename)
            count = self.encoder.estimate_qr_count(file_size, filename, estimated_compressed_size)
            max_qr_limit = 65535
            
            if count > max_qr_limit:
                messagebox.showerror("错误", f"文件过大，需要{count}个二维码，超出编码能力上限{max_qr_limit}个。请减小文件大小或选择其他传输方式。")
                return
        except Exception as e:
            messagebox.showerror("错误", f"无法预估二维码数量: {str(e)}")
            return
            
        # 清空之前的二维码
        qr_dir = "qr"
        if os.path.exists(qr_dir):
            for file in os.listdir(qr_dir):
                if file.startswith("qr_") and file.endswith(".png"):
                    os.remove(os.path.join(qr_dir, file))
                    
        # 禁用按钮
        self.generate_btn.config(state=tk.DISABLED)
        self.play_btn.config(state=tk.DISABLED)
        
        # 在新线程中生成二维码
        thread = threading.Thread(target=self._generate_qr_thread, args=(file_path, qr_dir))
        thread.daemon = True
        thread.start()
        
    def _generate_qr_thread(self, file_path, output_dir):
        """生成二维码的线程函数"""
        try:
            if self.logger:
                self.logger.info(f"开始生成二维码: {file_path}")
                
            # 更新进度条
            if self.progress_var:
                self.progress_var.set(0)
            if self.progress_label:
                self.progress_label.config(text="正在生成二维码...")
                
            # 获取文件信息
            file_size = os.path.getsize(file_path)
            filename = os.path.basename(file_path)
            
            if self.logger:
                self.logger.info(f"文件信息: {filename} ({file_size}字节)")
                self.logger.info(f"开始生成二维码，文件: {filename} ({file_size}字节)")

            # 生成二维码
            self.qr_files = self.encoder.encode_file_to_qr(file_path, output_dir, progress_callback=self._update_progress, logger=self.logger)

            if self.logger:
                self.logger.success(f"二维码生成完成，共生成 {len(self.qr_files)} 个二维码")
                
            # 更新UI
            self.parent.after(0, self._on_qr_generated)
            
        except Exception as e:
            error_msg = str(e)
            if self.logger:
                self.logger.error(f"生成二维码失败: {error_msg}")
            self.parent.after(0, lambda msg=error_msg: self._on_qr_error(msg))
            
    def _on_qr_generated(self):
        """二维码生成完成后的回调"""
        self.generate_btn.config(state=tk.NORMAL)
        self.play_btn.config(state=tk.NORMAL)
        if self.progress_var:
            self.progress_var.set(100)
        if self.progress_label:
            self.progress_label.config(text="二维码生成完成")
            
    def _estimate_compressed_size(self, file_path: str, file_size: int, filename: str) -> int:
        """
        预估压缩后的文件大小
        
        Args:
            file_path: 文件路径
            file_size: 原始文件大小
            filename: 文件名
            
        Returns:
            预估的压缩后大小
        """
        try:
            # 如果文件扩展名表明已经压缩，直接返回原始大小
            _, ext = os.path.splitext(filename.lower())
            if ext in self.encoder.NO_COMPRESSION_EXTENSIONS:
                return file_size
                
            # 对于小文件，直接读取并压缩以获得准确预估
            if file_size <= 1024 * 1024:  # 1MB以下
                with open(file_path, 'rb') as f:
                    data = f.read()
                compressed = __import__('zlib').compress(data)
                return len(compressed)
            
            # 对于大文件，采样压缩预估
            sample_size = min(1024 * 100, file_size // 10)  # 采样100KB或10%
            with open(file_path, 'rb') as f:
                sample_data = f.read(sample_size)
            
            compressed_sample = __import__('zlib').compress(sample_data)
            compression_ratio = len(compressed_sample) / len(sample_data)
            
            # 预估整个文件的压缩大小，考虑压缩效率随文件增大而降低
            estimated_size = int(file_size * compression_ratio * 1.1)  # 增加10%缓冲
            return max(1, min(estimated_size, file_size))  # 确保不小于1且不大于原始大小
            
        except Exception:
            # 预估失败时返回原始大小
            return file_size

    def _update_progress(self, progress):
        """更新进度条"""
        if self.progress_var:
            self.progress_var.set(progress)
        if self.progress_label:
            self.progress_label.config(text=f"正在生成... {progress:.1f}%")
    
    def _on_qr_error(self, error_msg):
        """二维码生成失败的回调"""
        self.generate_btn.config(state=tk.NORMAL)
        messagebox.showerror("错误", f"生成二维码失败: {error_msg}")
        if self.progress_var:
            self.progress_var.set(0)
        if self.progress_label:
            self.progress_label.config(text="生成失败")
            
    def play_qr(self):
        """播放二维码"""
        if not self.qr_files:
            messagebox.showwarning("警告", "没有可播放的二维码")
            return
            
        self.playing = True
        self.play_btn.config(state=tk.DISABLED)
        
        # 创建播放窗口
        self.play_window = tk.Toplevel(self.parent)
        self.play_window.title("二维码播放")
        self.play_window.geometry("800x800")
        self.play_window.protocol("WM_DELETE_WINDOW", self.stop_play)
        
        # 最大化窗口
        self.play_window.state('zoomed')
        
        # 创建标签显示二维码
        self.qr_label = ttk.Label(self.play_window)
        self.qr_label.pack(expand=True)
        
        # 开始播放线程
        self.play_thread = threading.Thread(target=self._play_qr_thread)
        self.play_thread.daemon = True
        self.play_thread.start()
        
    def _play_qr_thread(self):
        """播放二维码的线程函数"""
        current_index = 0
        total_count = len(self.qr_files)
        loop_count = 1
        
        while self.playing and self.qr_files:
            try:
                qr_file = self.qr_files[current_index]
                
                # 更新窗口标题显示当前序号、总数和循环轮数
                self.parent.after(0, lambda idx=current_index+1, total=total_count, loop=loop_count: 
                    self.play_window.title(f"二维码播放 - {idx}/{total} (第{loop}轮)"))
                
                # 加载并显示二维码
                image = Image.open(qr_file)
                
                # 调整图片大小以适应窗口
                window_width = self.play_window.winfo_width()
                window_height = self.play_window.winfo_height()
                
                if window_width > 100 and window_height > 100:  # 确保窗口已显示
                    # 计算合适的图片大小（留出一些边距）
                    max_size = min(window_width - 40, window_height - 40)
                    image.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
                    
                    photo = ImageTk.PhotoImage(image)
                    
                    # 更新UI
                    self.parent.after(0, lambda p=photo: self._update_qr_display(p))
                
                # 等待5秒
                time.sleep(5)
                
                # 下一个二维码
                current_index = (current_index + 1) % len(self.qr_files)
                
                # 如果回到第一个二维码，增加循环计数
                if current_index == 0:
                    loop_count += 1
                
            except Exception as e:
                if self.logger:
                    self.logger.error(f"播放二维码出错: {str(e)}")
                break
                
    def _update_qr_display(self, photo):
        """更新二维码显示"""
        if hasattr(self, 'qr_label'):
            self.qr_label.config(image=photo)
            self.qr_label.image = photo  # 保持引用
    
    def stop_play(self):
        """停止播放"""
        self.playing = False
        self.play_btn.config(state=tk.NORMAL)
        
        if hasattr(self, 'play_window'):
            self.play_window.destroy()
            delattr(self, 'play_window')
    
    def _check_existing_qr_files(self):
        """检查是否存在现有的二维码文件"""
        qr_dir = "qr"
        if os.path.exists(qr_dir):
            # 获取所有二维码文件
            qr_files = []
            for file in os.listdir(qr_dir):
                if file.startswith("qr_") and file.endswith(".png"):
                    qr_files.append(os.path.join(qr_dir, file))
            
            # 按文件名排序
            qr_files.sort()
            
            if qr_files:
                self.qr_files = qr_files
                self.play_btn.config(state=tk.NORMAL)
                if self.logger:
                    self.logger.info(f"发现 {len(qr_files)} 个现有二维码文件")
            else:
                self.qr_files = []
                self.play_btn.config(state=tk.DISABLED)
        else:
            self.qr_files = []
            self.play_btn.config(state=tk.DISABLED)